package com.googlecode.dgwt.client.dojo.data.model;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import com.googlecode.dgwt.client.dijit.View;
import com.googlecode.dgwt.client.dojo.data.base.ModeledStore;
import com.googlecode.dgwt.client.dojo.data.base.Store;
import com.googlecode.dgwt.client.dojo.data.listener.ModelListener;
import com.googlecode.dgwt.client.dojo.exception.AttachException;
import com.googlecode.dgwt.client.dojo.util.AttachList;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public abstract class StoreModelBase<Item,Column extends StoreColumn<Item,?>> implements StoreModel<Item,Column> {
    

    private Set<ModeledStore<Item>> storeList = new HashSet<ModeledStore<Item>>();
    private ColumnList columnList;
    
    
    public StoreModelBase() {
        this.columnList = createColumnList(10);
    }
    
    public StoreModelBase(int capacity) {
        this.columnList = createColumnList(capacity);
    }
    
    public StoreModelBase(Collection<? extends Column> columnList) {
        if (columnList != null) {
            this.columnList = createColumnList(columnList.size());
            this.columnList.addAll(columnList);
        } else {
            this.columnList = createColumnList(10);
        }
    }
    
    
    @Override
    public void onAttach(Store<Item> store) throws AttachException {
        if (!(store instanceof ModeledStore<?>)) {
            throw new ModelAttachException("Only descendents of ModeledStore can use a StoreModel");
        }
        this.storeList.add((ModeledStore<Item>)store);
    }

    
    @Override
    public void onDetach(Store<Item> store) {
        this.storeList.remove(store);
    }

    
    @Override
    public boolean isAttachedTo(Store<?> store) {
        return this.storeList.contains(store);
    }
    
    protected void onColumnChanged(StoreColumn<Item, ?> column) {
        for (ModeledStore<Item> store : storeList) {
            if (this == store.getModel()){
                for (ModelListener<Item> listener : store.getModelListeners()) {
                    listener.onColumnChanged(column);
                }
            }
        }
    }
    
    protected void onColumnAdded(StoreColumn<Item, ?> column) {
        for (ModeledStore<Item> store : storeList) {
            if (this == store.getModel()){
                for (ModelListener<Item> listener : store.getModelListeners()) {
                    listener.onColumnAdded(column);
                }
            }
        }
    }
    
    protected void onColumnRemoved(StoreColumn<Item, ?> column) {
        for (ModeledStore<Item> store : storeList) {
            if (this == store.getModel()){
                for (ModelListener<Item> listener : store.getModelListeners()) {
                    listener.onColumnRemoved(column);
                }
            }
        }
    }

    protected void onColumnReplaced(int columnIndex, StoreColumn<Item, ?> oldColumn, StoreColumn<Item, ?> newColumn) {
        for (ModeledStore<Item> store : storeList) {
            if (this == store.getModel()){
                for (ModelListener<Item> listener : store.getModelListeners()) {
                    listener.onColumnReplaced(columnIndex, oldColumn, newColumn);
                }
            }
        }
    }

    protected void onColumnRefresh(StoreColumn<Item, ?> column) {
        for (ModeledStore<Item> store : storeList) {
            if (this == store.getModel()){
                for (ModelListener<Item> listener : store.getModelListeners()) {
                    listener.onColumnRefresh(column);
                }
            }
        }
    }

    @Override
    public void onRegisterParent(View view, boolean registered) {
        for (Column column : getColumns()){
            column.onRegisterParent(view, registered);
        }
    }


    @Override
    @SuppressWarnings("unchecked")
    public <C extends Column> ColumnSet<C> getColumns() {
        return (ColumnSet<C>) columnList;
    }    
    

    @Override
    public Iterable<ModeledStore<Item>> getAttachedStores() {
        return storeList;
    }

    protected ColumnList createColumnList(int initialCapacity) {
        return new ColumnList(initialCapacity);
    }

    
    public class ColumnList extends AttachList<StoreModel<Item,?>, Column> implements ColumnSet<Column> {

        protected ColumnList(int initialCapacity) {
            super(StoreModelBase.this, initialCapacity);
        }

        @Override
        public ColumnList append(Column column) {
            add(column);
            return this;
        }

        @Override
        protected void onAfterAdd(int index, Column item) {
            super.onAfterAdd(index, item);
            onColumnAdded(item);
        }

        @Override
        protected void onModified(int index, Column item) {
            super.onModified(index, item);
            onColumnChanged(item);
        }

        @Override
        protected void onAfterRemove(int index, Column item) {
            super.onAfterRemove(index, item);
            onColumnRemoved(item);
        }

        @Override
        protected void onAfterReplace(int index, Column oldItem, Column newItem) {
            super.onAfterReplace(index, oldItem, newItem);
            onColumnReplaced(index, oldItem, newItem);
        }        
    }
}
